package com.yuxl.common.file.utils;

import com.yuxl.common.core.domain.R;
import com.yuxl.common.core.handler.ApiException;
import com.yuxl.common.file.config.MinioParameter;
import com.yuxl.common.file.domain.ViewContentTypeEnum;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.ObjectWriteResponse;
import io.minio.PutObjectArgs;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;

/**
 * @Author : yuxl
 * @create 2025/8/15 下午7:24
 */
@Slf4j
@Component
public class MinioFileUtil {

    @Resource
    private MinioParameter minioParameter;


    public MinioClient getClient() {
        return MinioClient.builder()
                        .endpoint(minioParameter.getEndpoint())
                        .credentials(minioParameter.getAccessKey(), minioParameter.getSecretKey())
                        .build();
    }

    public Mono<String> putObject(FilePart filePart, String path,String fileName,String contentType){
        return filePart.content()
                .collectList()  // 收集所有数据块为列表
                .flatMap(dataBuffers -> {
                    ObjectWriteResponse objectWriteResponse = null;
                    try {
                        // 合并数据块为字节数组
                        byte[] bytes = new byte[dataBuffers.stream()
                                .mapToInt(DataBuffer::readableByteCount)
                                .sum()];
                        int offset = 0;
                        for (DataBuffer buffer : dataBuffers) {
                            int len = buffer.readableByteCount();
                            buffer.read(bytes, offset, len);
                            offset += len;
                        }
                        return Mono.fromCallable(() -> putObject(path,fileName,contentType, new ByteArrayInputStream(bytes), bytes.length, -1)).flatMap(object->{
                            if(object ==  null){
                                return Mono.error( new ApiException("上传文件失败"));
                            }
                            return Mono.just(minioParameter.getEndpoint() + "/" + minioParameter.getBucketName() + "/" + object.object());
                        });
                    } finally {
                        // 使用 DataBufferUtils 释放所有缓冲区资源
                        dataBuffers.forEach(DataBufferUtils::release);
                    }
                });
    }

    public ObjectWriteResponse putObject(String path,String fileName,String contentType, InputStream stream, long objectSize, long partSize){
        try {
            log.info("开始上传文件:{}",ViewContentTypeEnum.getContentType(fileName));
            return getClient().putObject(
                    PutObjectArgs
                            .builder()
                            .bucket(minioParameter.getBucketName())
                            .object(path+fileName)
                            .contentType(contentType)
                            .stream(stream,objectSize,partSize)
                            .build()
            );
        }catch (MinioException | IOException | NoSuchAlgorithmException | InvalidKeyException e) {
                log.error("Error occurred: " + e);
        }
        return null;
    }


    public Mono<String> getUploadUrl(String fileName) {
        return Mono.fromCallable(() -> {
            // 生成预签名 URL，有效期 30 分钟
            return getClient().getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.PUT)
                            .bucket(minioParameter.getBucketName())
                            .object(fileName)
                            .expiry(30, TimeUnit.MINUTES)
                            .build());
        });
    }
}
